package com.livestocksolutions;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;

import com.livestocksolutions.util.UpdateHistory;

/* -- Version History --
 *  1.0 : 12 July 2012 - Initial version.
 *  
 *  1.1 : 12 July 2012 - Changed return type of getPriority() from Type String to type int
 *                        Added method getPriorityString(int) to return the String of the
 *                        priority value.
 *                       Changed setPriority(String) to setPriority(int) to directly accept
 *                        an integer value that corresponds to either RESOLVED, OBSERVE,
 *                        NONCRITICAL, WATCH, CRITICAL or URGENT. Values greater than URGENT
 *                        will be considered the same as URGENT.
 *                       getModified() and setUnModified() to check and reset if this Object
 *                        has been modified since setUnModified() was called or the creation
 *                        of the Object.
 *  
 *  1.2 : 16 July 2012 - Class now implements Serializable
 *  
 *  1.3 : 05 September 2012 - Added private long modifiedTime, private boolean modified and private final long id
 *                       public long getID(), public boolean getModified() and public long getModifiedDate()
 *                       
 *  1.4 : 22 September 2012 - Added methods and attributes to track changes. Removed modified attributes introduced
 *                       in previous version.
 *                       Serialisation method no longer requires MedicalHistory implementing Serializable.
 */

/**
 * An Observation that contains the date the observation was on,
 * the observation itself and the priority level of this Observation.
 * @author Adam Parr
 * @version 1.4
 * 
 * @.modified 22 September 2012
 */
public class Observation{
	/**
	 * The Owner is of type Bovine
	 */
	protected static final int BOVINE = 0;
	/**
	 * The Owner is of type Group
	 */
	protected static final int GROUP = 1;
	/**
	 * The Owner is of type Location
	 */
	protected static final int LOCATION = 2;
	
	/**
	 * For Observations that are no-longer considered to be active.
	 */
	public static final int RESOLVED = 0;
	/**
	 * Level 1 : An Observation will have no or very little priority .
	 */
	public static final int OBSERVE = 1;
	/**
	 * Level 2 : An Observation will have a low priority, but still just a comment.
	 */
	public static final int NONCRITICAL = 2;
	/**
	 * Level 3 : An Observation will have normal priority, Observations with this level priority
	 * or above will display in the provided space on the home screen.
	 */
	public static final int WATCH = 3;
	/**
	 * Level 4 : An Observation will have a high priority.
	 */
	public static final int CRITICAL = 4;
	/**
	 * Level 5 : An Observation will have the highest priority.
	 */
	public static final int URGENT = 5;
	
	/**
	 * Unique ID that identifies this Owner
	 */
	protected final long id;

	/**
	 * Keep track of any changes of this Location.
	 */
	protected HashMap<String,UpdateHistory> update = new HashMap<String,UpdateHistory>();
	
	/**
	 * Date of the Observation
	 */
	protected long date;
	/**
	 * The Observation itself
	 */
	protected String comment;
	/**
	 * Priority level of the comment.<br>
	 * One of: RESOLVED, OBSERVE, NONCRITICAL, WATCH, CRITICAL or URGENT.
	 */
	protected int priority;
	
	/**
	 * Store the Owners ID.
	 */
	protected long owner;
	/**
	 * The Type of the Owner
	 */
	protected int type;
	
	/**
	 * @param date Date of the Observation
	 * @param comment The Observation
	 * @param priority The Priority level of this Observation
	 */
	public Observation(Date date, String comment, int priority){
		this.date=date.getTime();
		this.comment=comment;
		this.priority=priority;
		
		long tempID = Calendar.getInstance().getTimeInMillis();
		while(LivestockSolutions.getObservationByID(tempID)!=null){
			// Just in case multiple items of this type are made in the same millisecond
			tempID++;
		}
		id=tempID;
		
		update.put("ID", new UpdateHistory(String.valueOf(id)));
		update.put("DATE", new UpdateHistory(String.valueOf(this.date)));
		update.put("PRIORITY", new UpdateHistory(String.valueOf(this.priority)));
		update.put("COMMENT", new UpdateHistory(this.comment));
	}
	
	
	/**
	 * Get the date of this Observation
	 * @return The date this Observation was observed on.
	 */
	public Date getDate(){
		return date==0?null:new Date(date);
	}
	
	/**
	 * Get the comment from this Observation
	 * @return The comment from this Observation
	 */
	public String getComment(){
		return comment;
	}
	
	/**
	 * Get the Priority level of this Observation
	 * @return priority level of this Observation
	 */
	public int getPriority(){
		return priority;
	}
	
	/**
	 * 
	 * @param priority level of importance
	 * @return String representation of this importance level
	 */
	public static String getPriorityString(int priority){
		if(priority<OBSERVE){
			// priority is less than OBSERVE level, so it is RESOLVED
			return "Resolved";
		}else if(priority<NONCRITICAL){
			// priority is less than NONCRITICAL level, so it is OBSERVE
			return "Observe";
		}else if(priority<WATCH){
			// priority is less than WATCH level, so it is NONCRITICAL
			return "Non-Critical";
		}else if(priority<CRITICAL){
			// priority is less than CRITICAL level, so it is WATCH
			return "Watch";
		}else if(priority<URGENT){
			// priority is less than URGENT level, so it is CRITICAL
			return "Critical";
		}else{
			// priority is URGENT or greater than, so it is URGENT
			return "Urgent";
		}
	}
	
	/**
	 * Gets the Owner associated with this Observation
	 * @return The Owner as an Object. It has to be up to the calling method
	 * to determine the correct type of the given owner
	 */
	public Object getOwner(){
		switch(type){
		case BOVINE:
			return LivestockSolutions.getBovineByID(owner);
		case GROUP:
			return LivestockSolutions.getGroupByID(owner);
		case LOCATION:
			return LivestockSolutions.getLocationByID(owner);
		}
		return null;
	}
	
	/**
	 * Change the current observation to something different.
	 * @param comment new comment to update the current observation to.
	 */
	public void setComment(String comment){
		if(!this.comment.equals(comment)){
			this.comment=comment;
			update.put("COMMENT", new UpdateHistory(comment));
		}
	}
	
	/**
	 * Change the priority of this comment
	 * @param priority The priority level of this comment
	 * @see #RESOLVED
	 * @see #OBSERVE
	 * @see #NONCRITICAL
	 * @see #WATCH
	 * @see #CRITICAL
	 * @see #URGENT
	 */
	public void setPriority(int priority){
		if(this.priority!=priority){
			this.priority=priority;
			update.put("PRIORITY", new UpdateHistory(String.valueOf(priority)));
		}
	}

	/**
	 * Get the unique id of this Observation
	 * @return the id of this Observation
	 */
	public long getID(){
		return id;
	}
	
	/**
	 * Set the owner of this Observation to a Bovine type
	 * @param owner The Bovine that this Observation belongs to
	 * @return <tt>true</tt> if no owner was set for this Observation, and the
	 * given owner was then set as this observations owner, <tt>false</tt> otherwise.
	 */
	public boolean setOwner(Bovine owner){
		if(this.owner==0){
			this.owner = owner.getID();
			type = BOVINE;
			update.put("OWNER", new UpdateHistory(String.valueOf(this.owner)));
			update.put("TYPE", new UpdateHistory(String.valueOf(this.type)));
			return true;
		}
		return false;
	}
	
	/**
	 * Set the owner of this Observation to a Location type
	 * @param owner The Location that this Observation belongs to
	 * @return <tt>true</tt> if no owner was set for this Observation, and the
	 * given owner was then set as this observations owner, <tt>false</tt> otherwise.
	 */
	public boolean setOwner(Location owner){
		if(this.owner==0){
			this.owner = owner.getID();
			type = LOCATION;
			update.put("OWNER", new UpdateHistory(String.valueOf(this.owner)));
			update.put("TYPE", new UpdateHistory(String.valueOf(this.type)));
			return true;
		}
		return false;
	}
	
	/**
	  * Set the owner of this Observation to a Group type
	 * @param owner The Group that this Observation belongs to
	 * @return <tt>true</tt> if no owner was set for this Observation, and the
	 * given owner was then set as this observations owner, <tt>false</tt> otherwise.
	 */
	public boolean setOwner(Group owner){
		if(this.owner==0){
			this.owner = owner.getID();
			type = GROUP;
			update.put("OWNER", new UpdateHistory(String.valueOf(this.owner)));
			update.put("TYPE", new UpdateHistory(String.valueOf(this.type)));
			return true;
		}
		return false;
	}
	
	/**
	 * Updates this Object with the updated values from the different version.
	 * @param changes Observation that represents this Object in a different version.
	 * @return <tt>true</tt> if changes is not null and changes does not equal this object, <tt>false</tt> otherwise.
	 */
	public boolean update(Observation changes){
		if(changes!=null && this!=changes){
			if(updateBefore("DATE",changes)){
				date=Long.parseLong(changes.update.get("DATE").getValue());
				update.put("DATE", new UpdateHistory(String.valueOf(this.date)));
			}
			
			if(updateBefore("COMMENT",changes)){
				comment=(changes.update.get("COMMENT").getValue());
				update.put("COMMENT", new UpdateHistory(this.comment));
			}
			
			if(updateBefore("PRIORITY",changes)){
				priority=Integer.parseInt(changes.update.get("PRIORITY").getValue());
				update.put("PRIORITY", new UpdateHistory(String.valueOf(this.priority)));
			}
			
			if(updateBefore("OWNER",changes)){
				owner=Long.parseLong(changes.update.get("OWNER").getValue());
				update.put("OWNER", new UpdateHistory(String.valueOf(this.owner)));
			}
			
			if(updateBefore("TYPE",changes)){
				type=Integer.parseInt(changes.update.get("TYPE").getValue());
				update.put("TYPE", new UpdateHistory(String.valueOf(this.type)));
			}
			return true;
		}
		return false;
	}
	
	/**
	 * Checks to see if the modification date of the item at this position is before the changed value
	 * @param key The String key at which to compare the modification dates of the modified objects.
	 * @param changes The Observation that represents this Observation on another system.
	 * @return <tt>true</tt> if the old value was modified before the changed value. <tt>false</tt> otherwise.
	 */
	protected boolean updateBefore(String key,Observation changes){
		if(this.update.get(key)==null && changes.update.get(key)!=null){
			return true;
		}else if(changes.update.get(key)==null){
			return false;
		}
		return !this.update.get(key).getDate().after(changes.update.get(key).getDate());
	}
}
